package com.woniuxy.woniuticket.ordermodule.service.serviceimpl;

import com.woniuxy.woniuticket.financemodule.dao.CinemaAccountDAO;
import com.woniuxy.woniuticket.movieinfomodule.dao.CinemaVipDAO;
import com.woniuxy.woniuticket.movieinfomodule.dao.UserVipInfoDAO;
import com.woniuxy.woniuticket.movieinfomodule.dao.UsersInfoDAO;
import com.woniuxy.woniuticket.moviemodule.dao.CinemaHallDAO;
import com.woniuxy.woniuticket.moviemodule.dao.CinemaMoviesDAO;
import com.woniuxy.woniuticket.moviemodule.dao.CinemaScheduleDAO;
import com.woniuxy.woniuticket.moviemodule.dao.CinemaSeatsDAO;
import com.woniuxy.woniuticket.moviemodule.service.CinemaSeatsService;
import com.woniuxy.woniuticket.ordermodule.constant.CinemaStreamConstant;
import com.woniuxy.woniuticket.ordermodule.constant.OrderConstant;
import com.woniuxy.woniuticket.ordermodule.dao.CinemaOderDAO;
import com.woniuxy.woniuticket.ordermodule.dao.CinemaOrderSeatDAO;
import com.woniuxy.woniuticket.ordermodule.dao.CinemaStreamDAO;
import com.woniuxy.woniuticket.ordermodule.dto.CartDTO;
import com.woniuxy.woniuticket.ordermodule.dto.OrderDTO;
import com.woniuxy.woniuticket.ordermodule.service.OrderService;
import com.woniuxy.woniuticket.ordermodule.utils.CompareTimeUtil;
import com.woniuxy.woniuticket.ordermodule.utils.OrderNumberUtil;
import com.woniuxy.woniuticket.ordermodule.utils.Order_InfoWrapperUtil;
import com.woniuxy.woniuticket.ordermodule.utils.Order_TicketStatusChangeUtil;
import com.woniuxy.woniuticket.pojo.*;
import com.woniuxy.woniuticket.usermodule.dao.UsersLoginDAO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.annotation.Resources;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by Wu xianbo on 2019/6/1
 * 订单service实现类
 */

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Resource
    private CinemaAccountDAO cinemaAccountDAO;  //商家账户dao
    @Resource
    private CinemaOderDAO cinemaOderDAO;    //订单dao
    @Resource
    private CinemaOrderSeatDAO cinemaOrderSeatDAO;   //座次中间表dao
    @Resource
    private CinemaVipDAO cinemaVipDAO;   //用户vip dao
    @Resource
    private UsersInfoDAO usersInfoDAO;   //用户信息详情dao
    @Resource
    private UsersLoginDAO usersLoginDAO;  //用户登录表dao
    @Resource
    private CinemaScheduleDAO cinemaScheduleDAO;  //排片信息dao
    @Resource
    private CinemaHallDAO cinemaHallDAO;   //影厅信息dao
    @Resource
    private CinemaSeatsDAO cinemaSeatsDAO;  //座位信息dao
    @Resource
    private CinemaMoviesDAO cinemaMoviesDAO;   //影片dao
    @Resource
    private Order_InfoWrapperUtil order_infoWrapperUtil;  //封装订单信息的工具类
    @Resource
    private CinemaStreamDAO cinemaStreamDAO;    //影院流水dao
    @Resource
    private CompareTimeUtil compareTimeUtil;    //时间比较工具类
    @Resource
    private OrderCache orderCache;      //redis缓存工具类
    @Resource
    private CinemaSeatsService cinemaSeatsService;    //座位校验service
    @Resource
    private UserVipInfoDAO userVipInfoDAO;      //通过userinfoid获取用户的折扣

    //创建订单(需要传入排片id、会员折扣率、票数)
    @Transactional
    @Override
    public CinemaOder creatOrder(CartDTO cartDTO) {
        //1：根据该订单中的排片id和座位号查询redis缓存中该座位存不存在，不存在则在缓存中加入座位信息
        List<String> originseats = cartDTO.getSeatNumbers();        //获取原座位数据
        List<CinemaSeats> cinemaSeats = new ArrayList<>();          //封装座位对象的集合
        //根据排片id查询排片表的信息
        CinemaSchedule cinemaSchedule = cinemaScheduleDAO.selectByPrimaryKey(cartDTO.getScheduleId());
        //封装单个座位对象
        for(String string:originseats){
            int num = Integer.parseInt(string);
            int seatRow = (int) (Math.floor(num/17)+1);
            int seatColumn = (int) (num - Math.floor(num/17)*17);
            CinemaSeats cinemaSeat = new CinemaSeats();
            cinemaSeat.setSeatrow(seatRow);                        //排数
            cinemaSeat.setSeatcolumn(seatColumn);                  //列数
            cinemaSeat.setScheduleId(cartDTO.getScheduleId());     //排片id
            cinemaSeat.setHallId(cinemaSchedule.getHallId());      //影厅id
            //装入座位对象集合
            cinemaSeats.add(cinemaSeat);
        }
        Boolean result = cinemaSeatsService.seatCheck(cinemaSeats);
        System.out.println("占座结果："+result);
        if(!result){
            return null;      //该座位已被占
        }
        //2：redis暂时锁定座位
        cinemaSeatsService.insertCinemaSeatsToRedis(cinemaSeats);
        //3:该座位没有被占用则执行下面的操作
        CinemaOder cinemaOder = new CinemaOder();                       //创建order对象
        String orderNumber = OrderNumberUtil.getUniqueOrderNumber();    //手动创建订单号
        cinemaOder.setOrdernum(orderNumber);                  //绑订订单号
        cinemaOder.setScheduleId(cartDTO.getScheduleId());              //获取排片id
        cinemaOder.setTicketnum(cinemaSeats.size());      //获取购买票数
        cinemaOder.setUserInfoId(cartDTO.getUserInfoId());    //获取用户infoid
        System.out.println("创建订单生成的订单号是：" + orderNumber);
        //获取排片表的价格
        BigDecimal schdulePrice = cinemaSchedule.getScheduleprice();
        //获取用户的vip级别，计算价格。此处需要在session中获取用户对象
        BigDecimal vipCont = userVipInfoDAO.queryVipById(cartDTO.getUserInfoId());//获取用户的vip折扣，但用户可能没有办折扣
        //折后价格
        if(vipCont!=null){
            schdulePrice = schdulePrice.multiply(vipCont);  //存在折扣
        }
        //订单总价
        BigDecimal totalPrice = schdulePrice.multiply(new BigDecimal(cinemaOder.getTicketnum()));
        cinemaOder.setAdjustedprice(totalPrice);
        //插入订单
        cinemaOderDAO.insertSelective(cinemaOder);
        //4：redis中缓存订单(需要订单号和用户infoid，订单号中需要保存该订单的座位信息)
        //缓存中存放该订单信息
        orderCache.orderCacheInsert(orderNumber, cartDTO.getUserInfoId(), cinemaSeats);
        return cinemaOder;
    }

    //根据订单号查询订单详情
    @Override
    public OrderDTO findOrderByOrderNumber(String ordernum) {
        //根据订单id查询订单
        CinemaOder cinemaOder = cinemaOderDAO.selectByPrimaryKey(ordernum);
        UsersInfo usersInfo = null;
        OrderDTO orderDTO = new OrderDTO();
        if (cinemaOder != null) {
            //转换数据库查出来的订单的支付状态和票的状态
            Order_TicketStatusChangeUtil.changeOrder_TicketStatus(orderDTO, cinemaOder);
            //根据user_info_id 查询用户表的用户信息
            usersInfo = usersInfoDAO.selectByPrimaryKey(cinemaOder.getUserInfoId());
            //封装订单的用户信息
            BeanUtils.copyProperties(usersInfo, orderDTO);
            //封装其他订单信息
            orderDTO = order_infoWrapperUtil.wrapperOrderInfo(orderDTO, cinemaOder);
        }
        return orderDTO;
    }

    //商家查询所有订单
    @Override
    public List<OrderDTO> findAllOrder(Integer cpage, Integer psize) {
        List<CinemaOder> cinemaOders = cinemaOderDAO.findallOrders(cpage, psize);
        List<OrderDTO> orderDTOS = new ArrayList<>();
        if (cinemaOders.size() != 0) {
            for (CinemaOder cinemaOder : cinemaOders) {
                OrderDTO orderDTO = new OrderDTO();
                BeanUtils.copyProperties(cinemaOder, orderDTO);
                //将订单状态用文字描述
                Order_TicketStatusChangeUtil.changeOrder_TicketStatus(orderDTO, cinemaOder);
                orderDTOS.add(orderDTO);
            }
        }
        return orderDTOS;
    }

    //根据用户userinfoid查询用户所有订单
    @Override
    public List<OrderDTO> findAllOrderByUid(Integer userid, Integer orderType) {
        //根据用户id查询login表返回user对象
        //UsersLogin usersLogin = usersLoginDAO.selectByPrimaryKey(userid);
        //根据user对象的user_info_id属性查询info表
        UsersInfo usersInfo = usersInfoDAO.selectByPrimaryKey(userid);
        //根据user_info_id查询所有订单(查询的是除开未支付的所有订单)
        List<CinemaOder> cinemaOders = cinemaOderDAO.findallOrdersByUser_info_id(userid, orderType);
        //封装数据的集合
        List<OrderDTO> orderDTOS = new ArrayList<>();
        if (cinemaOders.size() != 0) {
            for (CinemaOder cinemaOder : cinemaOders) {
                System.out.println("订单号：" + cinemaOder.getOrdernum());
                OrderDTO orderDTO = new OrderDTO();
                //转换数据库查出来的订单的支付状态和票的状态
                Order_TicketStatusChangeUtil.changeOrder_TicketStatus(orderDTO, cinemaOder);
                //封装订单信息
                orderDTO = order_infoWrapperUtil.wrapperOrderInfo(orderDTO, cinemaOder);
                orderDTOS.add(orderDTO);
            }
        }
        return orderDTOS;
    }

    //订单支付(需要传入订单号，座次表信息,用户infoid),第一个参数表示新订单号，第三个参数是从session中取出来的表示可能存在改签订单
    @Transactional
    @Override
    public CinemaOder payOrder(String orderNumber, Integer userInfoId, Object oldOrderNum) {
        System.out.println("进入service中支付订单");
        //1:正常支付订单的场景
        CinemaOder cinemaOder = new CinemaOder();
        cinemaOder.setOrdernum(orderNumber);
        //完善订单支付时间和订单状态
        //设置支付时间
        cinemaOder.setPaydate(new Date());
        //设置支付状态
        cinemaOder.setOderstatus(OrderConstant.OK_PAY);
        //更新订单表订单表
        cinemaOderDAO.updateByPrimaryKeySelective(cinemaOder);
        //插入座次表：1.查找排片表的hall_id.2.然后再将hall_id、座次信息插入座次表.3.最后插入座次中间表
        Integer schedule_id = cinemaOderDAO.selectByPrimaryKey(cinemaOder.getOrdernum()).getScheduleId();
        Integer hall_id = cinemaScheduleDAO.selectByPrimaryKey(schedule_id).getHallId();
        //从缓存中获取未支付订单
        OrderDTO newOrderDTO = orderCache.getOrderInfoByOrdernum(orderNumber, userInfoId);
        //需判断缓存中的订单是否还在
        if(newOrderDTO!=null){
            //获取座位信息
            List<CinemaSeats> seatList = newOrderDTO.getSeatList();
            //操作座次表和中间表
            for(CinemaSeats cinemaSeats:seatList){     //遍历预订单中的座位
                cinemaSeatsService.insertCinemaSeatsToMysql(cinemaSeats);          //删除redis中的临时座位
                cinemaSeatsDAO.insertSelective(cinemaSeats);                       //插入座位表
                System.out.println("影院座次表主键id是：" + cinemaSeats.getId());     //获取座次表的主键
                CinemaOrderSeat cinemaOrderSeat = new CinemaOrderSeat();    //创建座次中间表对象
                cinemaOrderSeat.setOderNum(cinemaOder.getOrdernum());       //座次中间表订单号
                cinemaOrderSeat.setSeatId(cinemaSeats.getId());              //座次中间表座次表主键
                cinemaOrderSeatDAO.insertSelective(cinemaOrderSeat);          //插入座次中间表
            }
            //插入影院账户表:1.查询当前订单合计.2.插入影院账户表
            BigDecimal orderPrice = cinemaOderDAO.selectByPrimaryKey(cinemaOder.getOrdernum()).getAdjustedprice();
            cinemaAccountDAO.increaseMoney(orderPrice);
            //插入影院流水表
            cinemaStreamDAO.insert(orderPrice, CinemaStreamConstant.INCOME);
            //移除redis缓存中的未支付订单
            orderCache.orderCacheDelete(orderNumber, userInfoId);
        }
        //2:存在改签情况的订单支付情况:1.原订单先退票、删除原订单
        if(oldOrderNum!=null){
            String oldOrderNumber = (String) oldOrderNum;  //原订单号
            CinemaOder oldcinemaOder = new CinemaOder();
            oldcinemaOder.setOrdernum(oldOrderNumber);
            //原订单退票
            this.returnOrder(oldcinemaOder,null,null,userInfoId);
            //原订单删除
            cinemaOderDAO.deleteByPrimaryKey(oldOrderNumber);
            //将改签订单的状态改为改签状态
            CinemaOder changeOrder = new CinemaOder();
            changeOrder.setOrdernum(orderNumber);                     //改签订单的订单号
            changeOrder.setOderstatus(OrderConstant.HAVE_CHANGEED);   //将改签订单状态改为已改签
            cinemaOderDAO.updateByPrimaryKeySelective(changeOrder);   //执行操作
        }
        return cinemaOder;
    }

    //订单改签(需要原订单号、新的排片id、座位号、票数、会员折扣率)- - - 》该方法已没有用了
    @Transactional
    @Override
    public CinemaOder changeOrder(CinemaOder cinemaOder) {
            //开始改签
            //查看新排片id的价格(需要传递scheduleid)
            CinemaSchedule cinemaSchedule = cinemaScheduleDAO.selectByPrimaryKey(cinemaOder.getScheduleId());
            BigDecimal scheddulePrice = cinemaSchedule.getScheduleprice();
            //计算折扣价(获取会员折扣率)
            scheddulePrice = scheddulePrice.multiply(new BigDecimal("0.8"));
            //计算新订单总价（此处需要票数）
            BigDecimal newTotalPrice = scheddulePrice.multiply(new BigDecimal("" + cinemaOder.getTicketnum()));
            //查询原订单总价
            BigDecimal oldTotalPrice = cinemaOderDAO.selectByPrimaryKey(cinemaOder.getOrdernum()).getAdjustedprice();
            //计算差价
            BigDecimal difPrice = newTotalPrice.subtract(oldTotalPrice);
            log.info("原价是：{},现价是：{}，差价是：{}", oldTotalPrice, newTotalPrice, difPrice);
            if (difPrice.compareTo(new BigDecimal("0")) > 0) {
                //需要补差价。影院账户加钱
                cinemaAccountDAO.increaseMoney(difPrice);
                //流水表账户插入记录
                cinemaStreamDAO.insert(difPrice, CinemaStreamConstant.INCOME);
            } else if (difPrice.compareTo(new BigDecimal("0")) < 0) {
                //需要退用户钱。影院账户减钱
                cinemaAccountDAO.decreaseMoney(difPrice);
                //流水表账户插入记录
                cinemaStreamDAO.insert(difPrice, CinemaStreamConstant.BACKCOME);
            }
            //修改订单信息
            cinemaOder.setScheduleId(2);   //新的排片id
            cinemaOder.setOderstatus(OrderConstant.HAVE_CHANGEED);     //设置订单为改签状态
            cinemaOder.setAdjustedprice(newTotalPrice);                //设置新的订单合计
            cinemaOderDAO.updateByPrimaryKeySelective(cinemaOder);     //跟新订单表
            //修改座次表信息
            //根据订单查询座次表id，可能是一个集合
            List<Integer> seatOrderIds = cinemaOrderSeatDAO.selectSeat_idByOrderNum(cinemaOder.getOrdernum());
            for(int i=0;i<seatOrderIds.size();i++){
                CinemaSeats cinemaSeats = new CinemaSeats();
                cinemaSeats.setId(seatOrderIds.get(i));
                cinemaSeats.setHallId(cinemaSchedule.getHallId());    //修改座次表schedule_id
                cinemaSeats.setSeatrow(11);              //修改排数
                cinemaSeats.setSeatcolumn(12);           //修改列数
                cinemaSeatsDAO.updateByPrimaryKeySelective(cinemaSeats);
            }
            return cinemaOder;
    }

    //订单退费（第一个参数为退费订单，第二个参数表示可能存在改签订单的退费,两个参数值一致，第三个参数表示存在改签订单时的新订单，需要支付）
    @Transactional
    @Override
    public int returnOrder(CinemaOder cinemaOder,Object oldOrderNum,String newOrderNum,Integer userinfo) {
        //1：正常退票流程：1.修改订单状态。2.修改账户表。3.修改座次表
        Integer row = 0;
        cinemaOder.setOderstatus(OrderConstant.RETURN_PAY);      //修改订单状态
        cinemaOderDAO.updateByPrimaryKeySelective(cinemaOder);   //提交订单状态
        cinemaOder = cinemaOderDAO.selectByPrimaryKey(cinemaOder.getOrdernum());  //查询当前订单详细信息
        cinemaAccountDAO.decreaseMoney(cinemaOder.getAdjustedprice());            //影院账户扣钱
        cinemaStreamDAO.insert(cinemaOder.getAdjustedprice(), CinemaStreamConstant.BACKCOME);  //流水表插入记录
        List<Integer> cinemaSeat_ids = cinemaOrderSeatDAO.selectSeat_idByOrderNum(cinemaOder.getOrdernum());  //查询座次表id
        row = cinemaOrderSeatDAO.deleteByorderNum(cinemaOder.getOrdernum());              //删除座次中间表
        for(int i=0;i<cinemaSeat_ids.size();i++){
            row = cinemaSeatsDAO.deleteByPrimaryKey(cinemaSeat_ids.get(i));                //删除座次表信息
        }

        //2：存在改签订单退费的追加流程：删除原订单、支付新订单
        if(oldOrderNum!=null && newOrderNum!=null && newOrderNum!=""){   //条件为真表示这是改签订单的退费
            String oldOrderNumber = (String) oldOrderNum;
            //删除原订单
            cinemaOderDAO.deleteByPrimaryKey(oldOrderNumber);
            //支付新订单(判断订单号不能为空)
            this.payOrder(newOrderNum,userinfo,null);
            //将改签订单的状态设置为改签状态
            CinemaOder changeorder = new CinemaOder();
            changeorder.setOrdernum(newOrderNum);                     //设置新的订单号
            changeorder.setOderstatus(OrderConstant.HAVE_CHANGEED);   //将订单状态改为改签状态
            cinemaOderDAO.updateByPrimaryKeySelective(changeorder);   //执行修改操作
        }
        return row;
    }

    //订单完成(扫描二维码取票情景)
    @Transactional
    @Override
    public int finishOrder(CinemaOder cinemaOder) {
        //根据订单号修改订单状态
        cinemaOder.setOderstatus(OrderConstant.ORDER_SUCCESS);
        return cinemaOderDAO.updateByPrimaryKeySelective(cinemaOder);
    }

    //订单失效（影片下架即调用该方法）
    @Transactional
    @Override
    public int orderFailure(Integer scheduleId) {
        //根据排片id找到所有的订单
        List<CinemaOder> cinemaOders = cinemaOderDAO.findallOrdersByscheduleId(scheduleId);
        //检查这些订单的状态
        for (CinemaOder c : cinemaOders) {
            if (c.getOderstatus().equals(OrderConstant.OK_PAY) || c.getOderstatus().equals(OrderConstant.HAVE_CHANGEED)) {
                CinemaOder cinemaOder = new CinemaOder();
                cinemaOder.setOrdernum(c.getOrdernum());
                cinemaOder.setOderstatus(OrderConstant.ORDER_FAILURE);
                //让该订单失效
                cinemaOderDAO.updateByPrimaryKeySelective(cinemaOder);
            }
        }
        return 1;
    }

    //用户退票前前先校验票的状态
    @Override
    public boolean checkReturnCondition(String orderNum) {
        CinemaOder cinemaOder = new CinemaOder();
        cinemaOder.setOrdernum(orderNum);
        //检查当前订单是否符合退费时间
        Boolean timeResult = compareTimeUtil.compareScheduleTime(cinemaOder);
        //检查当前订单的订单状态
        Boolean statusResult = compareTimeUtil.checkReturnStaus(cinemaOder);
        if(timeResult && statusResult) {
            return true;
        }
        return false;
    }

    //用户改签前先检验票的状态
    @Override
    public boolean checkChangeCondition(String orderNum) {
        CinemaOder cinemaOder = new CinemaOder();
        cinemaOder.setOrdernum(orderNum);
        //检查是否符合改签条件(时间)
        Boolean timeResult = compareTimeUtil.compareScheduleTime(cinemaOder);
        //检查是否是支付状态
        Boolean statusResult = compareTimeUtil.checkChangeStaus(cinemaOder);
        if(timeResult&& statusResult){
            return true;
        }
        return false;
    }

}
